<?php
if ( !class_exists( "Classes_Cache" ) ) { 

/*

	Author:	(c) Cedric McMillan Junior

	Date:	March 15th 2012
	
	Aim:	Caching class using memcache and apc at present

*/

//-------------------------------------------------------------------------------------------------
class Classes_Cache
{
	# set this value to append to items
	private	$cache_version;

	# what type of cache are we using today? DEFAULT = memcache
	private	$cache_type;

	# pointer to memcache object
	private	$memcache			=	NULL;

	public function __construct($cache_version=0,$cache_type=CACHE_TYPE_MEMCACHE)
	{
		# create an instance of the cache object
		$this->cache_version		=	$cache_version;
		# test for bounds
		if	(!in_array($cache_type,array(CACHE_TYPE_MEMCACHE,CACHE_TYPE_MYSQL,CACHE_TYPE_APC,CACHE_TYPE_FILE)))
		{
			$cache_type				=	CACHE_TYPE_DEFAULT;
		}
		$this->cache_type			=	$cache_type;
	
		if	($cache_type == CACHE_TYPE_MEMCACHE)
		{
			# memcache object
			#if (class_exists('Memcache')) 
			#{
				$this->memcache	=	new Memcache;
				
			#	if	(strcasecmp($_SERVER['HTTP_HOST'],"localhost") == 0)
			#	{
					$this->memcache->connect("localhost",11211);
			#	}
			#	else
			#	{
			#		$this->memcache->connect(CACHE_SERVER_IP, 11211);
			#	} 
			#}
		}
	}






	# this calls the arbitrary function that returns the data we want to cache
	public function callUserFunction($function_pointer,$params)
	{
		$vars	=	explode(",", $params);
		return call_user_func_array($function_pointer,$vars);
	}
	
	
	
	
	

	# for each type do the correct query to establish  
	private function getCachedItem($key,$bForce)
	{
		# this rare case will mean we just get the item fresh again from the cache
		if	($bForce == true)
		{
			return	false;
		}
		else if	($this->cache_type == CACHE_TYPE_MEMCACHE)
		{
			return	$this->memcache->get($key);
		}
		else if ($this->cache_type == CACHE_TYPE_APC)
		{
			return	apc_fetch($key);
		}
		# *** to do - add database and file versions ***
		return false;
	}
		
	
	
	
	
	

	# for each type do the correct query to establish  
	private function setCachedItem($key,$cache_item,$timeout)
	{
		if	($this->cache_type == CACHE_TYPE_MEMCACHE)
		{
			return	$this->memcache->set($key, $cache_item, 0, $timeout);
		}
		else if ($this->cache_type == CACHE_TYPE_APC)
		{
		    return	apc_store($key, $cache_item, $timeout);
		}
		
		# *** to do - add mysql and file versions ***
		return false;
	} 







	# caching swiss army knife function (get it from cache or set it into the cache also)
	public function getOrSet($timeout,$cache_type,$cache_key, $function_pointer, $params="",$bForce=false)
	{
		# log the time
		if	(CACHE_DEBUG_MODE == true)
		{
			$type	=	"<font color=orange>SET</font> put in cache";
			$start	=	microtime(true);
		}
	
		# force function call if the specified cache is not in existance
		if	($this->cache_type == CACHE_TYPE_APC)
		{	
			if (apc_exists() == false)
			{
				$bForce	=	true;
			}
		}
		else if	($this->cache_type == CACHE_TYPE_MEMCACHE)
		{	
			if (empty($this->memcache))
			{
				$bForce	=	true;
			}
		}
		else if	(CACHE_ON == false)
		{
			$bForce	=	true;
		}
		


	
		# no cache or no caching allowed - just get the data required
		if	($bForce == true)
		{
			$cache_item		=	$this->callUserFunction($function_pointer,$params);
		}
		# we need to check or set here
		else
		{
			# get unique key
			$key			=	md5($this->cache_version.$cache_type.$cache_key);
	
			# check if this item is not cached or if we are brute-forcing the issue 
			if	(($cache_item = $this->getCachedItem($key,$bForce)) == false)
			{
				$cache_item		=	$this->callUserFunction($function_pointer,$params);

				# check for null returned (just means it is pointless sending null to the cache, so get out)
				if	(empty($cache_item))
				{
					# log this?
					if	(CACHE_DEBUG_MODE == true)
					{
	addComment("getOrSet nothing returned from $function_pointer - couldnt set the item:$cache_type.$cache_key");
						return;
					}
				}
				
				# now, we have to set this item in the cahce
				if	(($this->setCachedItem($key,$cache_item, $timeout)) == false)
				{
					if	(CACHE_DEBUG_MODE == true)
					{
	    				$s	=	strlen($cache_item);
						# maybe we should log something here, or send an email to an administrator
	addComment("getOrSet ERROR, couldnt set the item:$cache_key (length:$s)");
					}
				}
			}
			else
			{	
				if	(CACHE_DEBUG_MODE == true)
				{
					# for logging only, but it shows that we got the item from the cache ok
					$type	=	"<font color=green>GET</font> from cache";
				}
			}
		}
		# log the time
		if	(CACHE_DEBUG_MODE == true)
		{
			$end		=	microtime(true);
			$elapsed	=	$end - $start;
			$elapsed	=	number_format($elapsed,6,".","");
	addComment("getOrSet $type TIME:$elapsed, item:$cache_type.$cache_key LIFETIME:$timeout seconds");
		}

		return	$cache_item;
	}
	
	
	
	
    

 






}




}


?>